#pragma once

#include <vector>
#include "common.h"
#include "LfsrRegister.h"
#include "Algebra.h"
#include "Matrix.h"

template<int size = 2>
class IRecurrenceOperator
{
public:
	//appply the operator to arbitrary square matrix of size size 
	//and store the result to the same matrix
	virtual void applyTo(Matrix<size> *a) const = 0;

	//appply the operator to vector of size size 
	//and store the result to the same vector
	virtual void applyTo(LfsrRegister<size> *a) const = 0;

	//create matrix corresponding to the operator
	virtual	Matrix<size>* toMatrix() const = 0;
};

template<int size = 2, int factorsSize = 2, class Category = right_operator_tag>
class MatrixOperator 
	: public IRecurrenceOperator<size>, RotatableInvoker<IAlgebra, Category>
{
	typedef RotatableInvoker<IAlgebra, Category> Multiplier;
public:
	typedef LfsrRegister<factorsSize> Factorset;
	typedef LfsrRegister<factorsSize + 1> ControlSequence;

	MatrixOperator(
		const IAlgebra *algebra,
		const ControlSequence &control,
		const Factorset &factors)
		:	_algebra(algebra),
			_control(control),
			_factors(factors)
	{
	}

	Matrix<size>* toMatrix() const
	{
		Matrix<size> *res = new Matrix<size>();
		for(int i = 0; i < factorsSize; i++)
		{
			res->set(
				size - 1, 
				_control.at(factorsSize - i),
				_factors.at(i));
		}
		for(int i = 1; i < size; i++)
		{
			res->set(i - 1, i, _algebra->unityElement());
		}
		return res;
	}

	void applyTo(Matrix<size> *q) const
	{
		//for each columns
		for(int i = 0; i < size; i++)
		{
			//Compute scalar product of the operator factor and the given vector
			//to get value of last element of the result
			BYTE t = _algebra->zeroElement();
			for(int j = 1; j < factorsSize + 1; j++)
			{
				BYTE k = Multiplier::op(
						_algebra,
						&IAlgebra::mult,
						_factors.at(j-1),
						q->row(_control.at(factorsSize - j + 1)).at(i));
				t = _algebra->add(t, k);
			}
			//initialize first vector element (it become last after shift)
			q->row(0).at(i) = t;
		}
		q->shiftUp();
	}

	void applyTo(LfsrRegister<size> *a) const
	{
		//Compute scalar product of the operator factor and the given vector
		//to get value of last element of the result
		BYTE t = _algebra->zeroElement();
		for(int j = 1; j < factorsSize + 1; j++)
		{
			BYTE k = Multiplier::op(
				_algebra,
				&IAlgebra::mult,
				_factors.at(j-1),
				a->at(_control.at(factorsSize - j + 1)));
			t = _algebra->add(t, k);
		}
		//shift vector up to one level
		a->rightShift();
		//initialize the last vector element
		a->at(size - 1) = t;
	}

	void setFactorset(const Factorset &value)
	{
		_factors = value;
	}

protected:
	const IAlgebra *_algebra;
	ControlSequence _control;
	Factorset _factors;
};

template
<
int size = 2, 
int factorsSize = 2, 
class Category = right_operator_tag
>
class MatrixSelfGenerator
{
public:

	typedef Matrix<size> State;
	typedef MatrixOperator<size, factorsSize, Category> Operator;
	typedef typename Operator::ControlSequence ControlSequence;
	typedef typename LfsrRegister<factorsSize> Configuration;

	MatrixSelfGenerator(
		const IAlgebra *algebra,
		const ControlSequence &control,
		const Configuration &factors)
		: _roperator (new Operator(algebra, control, factors))
	{
		_matrix = _roperator->toMatrix();
	}

	~MatrixSelfGenerator()
	{
		delete _matrix;
	}

	typename State next()
	{
		State res = *_matrix;
		_roperator->applyTo(_matrix);
		return res;
	}
	
	void setCongiguration(const Configuration &c)
	{
		_roperator->setFactorset(c);
		delete _matrix;
		_matrix = _roperator->toMatrix();
	}

	const Operator *recurrenceOperator() const
	{
		return _roperator;
	}

private:
	Matrix<size> *_matrix;
	Operator *_roperator;
};

template<int regSize, int factorsSize, class Category>	
struct generator_traits<MatrixSelfGenerator<regSize, factorsSize, Category>>
{
	typedef typename MatrixSelfGenerator<regSize, factorsSize, Category> Generator;
	typedef typename Generator::ControlSequence ControlSequence;
	typedef typename Generator::State State;
	typedef typename Generator::Configuration Configuration;
	typedef typename LfsrRegisterComparer<factorsSize> ConfigurationComparer;
	typedef typename std::less<Matrix<regSize>> StateComparer;
	static State next(Generator* gen) 
	{ 
		return gen->next(); 
	}
	static void setConfiguration(
		Generator* gen,
		const Configuration &c)
	{ 
		gen->setCongiguration(c);
	}
};
